package com.levy.contronller.javase.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;

/**
 * Created by levy on 2019/3/4.
 */
public class NioTest2 {

    //线程组
    public static WorkThread[] workThreads = new WorkThread[3];


    public static void main(String[] args) throws InterruptedException {
        //1.绑定端口
        //2.负责接受请求
        BossThread bossThread = new BossThread(12345);
        bossThread.start();

        //负责处理数据
        //3.处理请求的数据
        for (int i = 0; i < workThreads.length; i++) {
            workThreads[i] = new WorkThread();
            workThreads[i].start();
        }


        //main线程等待boss线程执行结束后再关闭
        bossThread.join();
    }


}

class BossThread extends Thread {

    private ServerSocketChannel server;
    private Selector selector;

    /**
     * 1. 绑定端口
     *
     * @param port
     */
    public BossThread(int port) {
        try {
            server = ServerSocketChannel.open();
            server.configureBlocking(false);
            server.bind(new InetSocketAddress(port));
            System.out.println("NIO启动");

            //selector 实现一个线程处理多个连接 多路复用
            //从操作系统底层 获取到所有连接中有数据的连接(事件通知)
            selector = Selector.open();
            //如果有新连接 就通过selector 通知
            server.register(selector, SelectionKey.OP_ACCEPT);


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 用来接受新连接
     */
    @Override
    public void run() {
        try {
            //不断获取新连接
            while (true) {
                //查询通知 连接数  1秒钟内受否有连接
                int count = selector.select(1000);
                if (count == 0) {
                    continue;
                }

                //符合条件的通知
                Set<SelectionKey> selectKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey next = iterator.next();
                    //判断是否是新连接
                    if (next.isAcceptable()) {
                        //获取到新连接 表明建立了连接 不代表有数据
                        SocketChannel clientSocketChannel = server.accept();
                        //显示设置为 非阻塞模式
                        clientSocketChannel.configureBlocking(false);

                        int random = new Random().nextInt(NioTest2.workThreads.length);
                        NioTest2.workThreads[random].processSocket(clientSocketChannel);

                        System.out.println(this.getName() + "有新连接了……");
                    }

                }

                //清空掉已经处理过的通知
                selectKeys.clear();
                selector.selectNow();


            }
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}


/**
 * 用来处理有数据传输的请求
 */
class WorkThread extends Thread {
    private Selector selector;

    public WorkThread() {
        try {
            selector = Selector.open();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    //不能直接处理请求  因为不代表有数据
    public void processSocket(SocketChannel socketChannel) throws ClosedChannelException {
        //继续交给selector处理 有数据了 进行通知
        socketChannel.register(selector, SelectionKey.OP_READ);


    }

    @Override
    public void run() {
        try {
            //不断获取新连接
            while (true) {
                //查询通知 连接数  1秒钟内受否有连接
                int count = selector.select(1000);
                if (count == 0) {
                    continue;
                }

                //符合条件的通知
                Set<SelectionKey> set = selector.selectedKeys();
                Iterator<SelectionKey> iterator = set.iterator();
                while (iterator.hasNext()) {
                    SelectionKey next = iterator.next();
                    //判断连接是否有数据
                    if (next.isReadable()) {
                        SocketChannel clientSocketChannel = (SocketChannel) next.channel();
                        clientSocketChannel.configureBlocking(false);

                        //读取请求内容
                        ByteBuffer bb = ByteBuffer.allocate(1024);


                        //业务处理流程代码
                        String request = new String(bb.array());
                        System.out.println(this.getName() + "收到请求数据……" + request);
                    }


                }


            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}